// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.android.apps.moderator;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.widget.Toast;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

/**
 * Base activity for most Activities in this application. Provides common
 * functionality such as application menus.
 */
public abstract class ModeratorActivity extends Activity {

  private static final String TAG = ModeratorActivity.class.getName();
  
  static final String API_URL_BASE = "http://www.googleapis.com/moderator/v1/";

  private static final int INPUT_STREAM_SIZE = 32 * 1024;
  private static final String PREFERENCE_KEY_BASE = "com.google.android.apps.moderator.";
  protected static final String PREFERENCE_KEY_EULA_AGREED = PREFERENCE_KEY_BASE + "eulaAgreed";
  protected static final String PREFERENCE_KEY_RECENT_SERIES = PREFERENCE_KEY_BASE + "recentSeries";
  protected static final int TOAST_DURATION = 1000 * 6;

  // Set by inheriting classes to indicate that an error does not mean close the activity.
  private boolean hasContent = false;
  
  /**
   * Displays a modal alert to the user
   */
  protected void alert(final int message) {
    //TODO: see if Activity.showDialog() does what I need
    runOnUiThread(new Runnable() {
      public void run() {
        if (!isFinishing()) {
          new AlertDialog.Builder(ModeratorActivity.this).setMessage(message).setNeutralButton(
              R.string.ok, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                  dialog.dismiss();
                  if (!hasContent) {
                    finish();
                  }
                }
              }).show();
        }
      }
    });
  }

  /**
   * Returns 'true' if the user is logged on. If the user is not logged on,
   * they will be taken to the login screen.
   */
  protected boolean ensureLogin() {
    String authToken = LoginActivity.getAuthToken(this);
    if ("".equals(authToken)) {
      Toast.makeText(this, getResources().getString(R.string.login_to_vote),
          ModeratorActivity.TOAST_DURATION).show();
      startActivity(new Intent(this, LoginActivity.class));
      return false;
    } else {
      return true;
    }
  }

  /**
   * Displays a general error to the user
   */
  protected void generalError(Exception e, int message) {
    Log.e(TAG, getText(message).toString(), e);
    hideProgress();
    alert(message);
  }
  
  /**
   * Get a correctly authenticated connection to the specified urlString
   */
  private HttpURLConnection getConnection(String urlString) throws IOException {
    URLConnection connection = new URL(urlString).openConnection();

    String authToken = LoginActivity.getAuthToken(this);
    if (!"".equals(authToken)) {
      connection.setRequestProperty("Authorization", "GoogleLogin auth=" + authToken);
    }
    connection.setRequestProperty("Content-Type", "application/json");
    return (HttpURLConnection) connection;
  }

  /**
   * Gets JSON data from specified URLs
   *
   * @param urlText The URL of the data to fetch
   * @return A parsed JSONObject from the data
   * @throws AccessException Thrown if a problem was experienced in the fetch
   */
  JSONObject getJsonData(String urlText) throws AccessException {
    try {
      URLConnection connection = getConnection(urlText);

      InputStream inputStream;
      try {
        inputStream = connection.getInputStream();
      } catch (NullPointerException e) {
        // Observed happening on pre 2.0 devices when there is an authentication problem
        throw new AuthenticationProblem(e);
      }
      BufferedReader reader =
          new BufferedReader(new InputStreamReader(inputStream), INPUT_STREAM_SIZE);
      StringBuffer builder = new StringBuffer();
      String line;
      while ((line = reader.readLine()) != null) {
        builder.append(line).append("\n");
      }
      return new JSONObject(builder.toString());
    } catch (FileNotFoundException e) {
      throw new AccessException(e, R.string.resource_not_found);
    } catch (IOException e) {
      throw new AccessException(e, R.string.connection_problem);
    } catch (JSONException e) {
      throw new AccessException(e, R.string.a_problem_occurred);
    }
  }

  /**
   * Remove the progress indicator
   */
  protected void hideProgress() {
    runOnUiThread(new Runnable() {
      public void run() {
        // All activities defined these standard components
        findViewById(R.id.Content).setVisibility(View.VISIBLE);
        findViewById(R.id.ProgressIndicatorContainer).setVisibility(View.GONE);
      }
    });
  }
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    showEulaIfUnsigned();
  }

  /**
   * Add the standard application menus
   */
  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {

    menu.clear(); // Clear any existing menu

    // 'Help' menu item
    MenuItem help = menu.add(R.string.help);
    help.setIcon(android.R.drawable.ic_menu_help);
    help.setOnMenuItemClickListener(new OnMenuItemClickListener() {
      public boolean onMenuItemClick(MenuItem item) {
        Intent intent = new Intent(ModeratorActivity.this, HelpActivity.class);
        startActivity(intent);
        return false;
      }
    });

    // Either a 'Sign in' or a 'Sign out' menu item, depending on the user's current status
    if ("".equals(LoginActivity.getAuthToken(this))) {
      MenuItem signIn = menu.add(R.string.sign_in);
      signIn.setIcon(R.drawable.ic_menu_login);
      signIn.setOnMenuItemClickListener(new OnMenuItemClickListener() {
        public boolean onMenuItemClick(MenuItem item) {
          startActivity(new Intent(ModeratorActivity.this, LoginActivity.class));
          return false;
        }
      });
    } else {
      MenuItem signOut = menu.add(R.string.sign_out);
      signOut.setIcon(R.drawable.ic_menu_login);
      signOut.setOnMenuItemClickListener(new OnMenuItemClickListener() {
        public boolean onMenuItemClick(MenuItem item) {
          LoginActivity.logOut(ModeratorActivity.this);
          runOnUiThread(new Runnable() {
            public void run() {
              Toast.makeText(ModeratorActivity.this, getResources().getString(R.string.signed_out),
                  TOAST_DURATION).show();
            }
          });
          return false;
        }
      });
    }
    
    return super.onPrepareOptionsMenu(menu);
  }

  /**
   * POST the specified JSONObject to the given URL
   * 
   * @param urlText The URL to POST the data to
   * @param object The JSONObject
   * @throws AccessException Thrown if there was a problem with the operation
   */
  public void postJsonData(String urlText, JSONObject object) throws AccessException {
    submitJSONData(urlText, object, "POST");
  }
  
  /**
   * PUT the specified JSONObject to the given URL
   * 
   * @param urlText The URL to PUT the data to
   * @param object The JSONObject
   * @throws AccessException Thrown if there was a problem with the operation
   */
  public void putJsonData(String urlText, JSONObject object) throws AccessException {
    submitJSONData(urlText, object, "PUT");
  }
  
  protected void setHasContent(boolean value) {
    hasContent = value;
  }
  
  /**
   * Prompt the user to sign the EULA unless they have already done so
   */
  private void showEulaIfUnsigned() {
    final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    if (!preferences.getBoolean(PREFERENCE_KEY_EULA_AGREED, false)) {
      AlertDialog.Builder eula = new AlertDialog.Builder(this);
      eula.setTitle(R.string.eula_title);
      eula.setIcon(android.R.drawable.ic_dialog_info);
      eula.setMessage(R.string.eula_text);
      eula.setPositiveButton(R.string.accept, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
          SharedPreferences.Editor editor = preferences.edit();
          editor.putBoolean(PREFERENCE_KEY_EULA_AGREED, true);
          editor.commit();
        }
      });
      eula.setNegativeButton(R.string.decline, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
          finish();
        }
      });
      eula.show();
    }
  }

  private void submitJSONData(String urlText, JSONObject object, String method)
      throws AccessException {
    try {
      HttpURLConnection connection = getConnection(urlText);
      connection.setDoOutput(true);
      connection.setRequestMethod(method);
      
      OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
      writer.write(object.toString());
      writer.close();

      try {
        int responseCode = connection.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK) {
          throw new AccessException(null, R.string.connection_problem);
        }
      } catch (NullPointerException e) {
        // Observed happening on pre 2.0 devices when there is an authentication problem
        throw new AuthenticationProblem(e);
      }
      
    } catch (IOException e) {
      e.printStackTrace();
      throw new AccessException(null, R.string.connection_problem);
    }
  }
  
  /**
   * Show the progress indicator
   */
  protected void showProgress() {
    runOnUiThread(new Runnable() {
      public void run() {
        findViewById(R.id.Content).setVisibility(View.GONE);
        findViewById(R.id.ProgressIndicatorContainer).setVisibility(View.VISIBLE);
      }
    });
  }

  
}
